import { Inject, Injectable } from '@nestjs/common';
import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm';
import { isEmpty } from 'lodash';
import { ApiException } from '@main/common/exceptions/api.exception';

// import SysDepartment from 'src/entities/admin/sys-department.entity';
// import SysUserRole from 'src/entities/admin/sys-user-role.entity';
import SysUser from '@main/entities/service/admin/sys/sys-user.entity';
import SysUserRole from '@main/entities/service/admin/sys/sys-user-role.entity';
import SysRole from '@main/entities/service/admin/sys/sys-role.entity';
import { UtilService } from '@main/shared/services/util.service';
import { EntityManager, In, Like, Not, Repository } from 'typeorm';
import { RedisService } from '@main/shared/services/redis.service';

// import { SYS_USER_INITPASSWORD } from 'src/common/contants/param-config.contants';
import { ConfigService } from '@nestjs/config';
import { ROOT_ROLE_ID } from '@main/service/modules/admin/admin.constants';
import { SysParamConfigService } from '../paramConfig/paramConfig.service';
import { SysOperationLogService } from '../operationLog/operationLog.service';

@Injectable()
export class SysUserService {
  constructor(
    @InjectRepository(SysUser) private userRepository: Repository<SysUser>,
    // @InjectRepository(SysDepartment)
    // private departmentRepository: Repository<SysDepartment>,
    @InjectRepository(SysUserRole)
    private userRoleRepository: Repository<SysUserRole>,
    @InjectRepository(SysRole)
    private roleRepository: Repository<SysRole>,
    private redisService: RedisService,
    private paramConfigService: SysParamConfigService,
    @InjectEntityManager() private entityManager: EntityManager,
    private configService: ConfigService,
    private operationLogService: SysOperationLogService,
    @Inject(ROOT_ROLE_ID) private rootRoleId: number,
    private util: UtilService
  ) {
    this.initAdmin();
  }

  // rootRoleId = this.rootRoleId;

  async initAdmin() {
    const roleList = await this.roleRepository.find();

    const userList = await this.userRepository.find();

    const userRoleList = await this.userRoleRepository.find();

    if (roleList.length === 0 && userList.length === 0 && userRoleList.length === 0) {
      // console.log('初始化数据')

      this.userRepository.insert({
        userName: 'admin',
        password: this.util.md5(`${'admin123'}${'dfd'}`),
        nickName: '超级管理员',
        psalt: 'dfd',
        status: 1,
        email: '',
        headImg: null,
        phone: '',
        remark: null,
        id: '1',
      });

      this.userRoleRepository.insert({
        userId: '1',
        roleId: '1',
        id: '1',
      });

      this.roleRepository.insert({
        id: '1',
        userId: '1',
        name: '超级管理员',
        remark: '',
      });

      this.roleRepository.insert({
        id: '2',
        userId: '1',
        name: '机长',
        remark: '',
      });
    }
  }

  async list() {
    const list = await this.userRepository.find({
      where: { id: Not(await this.findRootUserId()), status: 1 },
    });

    list.forEach((item) => {
      delete item.password;

      delete item.psalt;
    });

    return list;
  }

  /**
   * 根据用户名查找已经启用的用户
   */
  async findUserByUserName(userName: string): Promise<SysUser | undefined> {
    return await this.userRepository.findOne({
      where: { userName, status: 1 },
    });
  }

  async page(uid, data) {
    const { pageSize, pageNum, entity = {} } = data;

    const { userName, nickName, status } = entity;

    const where = {
      id: Not(In([await this.findRootUserId(), uid])),
      userName: Like(`%${userName}%`),
      nickName: Like(`%${nickName}%`),
    };

    if (status) where['status'] = status;

    const [list, total] = await this.userRepository.findAndCount({
      where,
      order: {
        updateTime: 'DESC',
      },
      take: pageSize,
      skip: (pageNum - 1) * pageSize,
    });

    list.forEach((item) => {
      delete item.password;

      delete item.psalt;
    });

    return [list, total];
  }

  /**
   * 增加系统用户，如果返回false则表示已存在该用户
   * @param data
   * @param user Object 对应SysUser实体类
   */
  async add(data, user, mac, ip, syncHostSqlite) {
    // const insertData: any = { ...CreateUserDto };
    const { userName } = data;

    const databaseOperationLogList = [];

    const exists = await this.userRepository.findOne({
      where: { userName },
    });
    if (!isEmpty(exists)) throw new ApiException(10001);

    // 所有用户初始密码为123456
    await this.entityManager.transaction(async (manager) => {
      const salt = this.util.generateRandomValue(32);

      // 查找配置的初始密码
      const initPassword = await this.paramConfigService.findValueByCode('initUserPassword');

      const { roles, userName, nickName, email, phone, remark, status } = data;

      const password = this.util.md5(`${initPassword ?? '123456'}${salt}`);
      const u = manager.create(SysUser, {
        userName,
        password,
        nickName,
        email,
        phone,
        remark,
        status,
        psalt: salt,
        createName: user.nickName,
        createUserName: user.userName,
        updateName: user.nickName,
        updateUserName: user.userName,
      });
      const result = await manager.save(u);

      databaseOperationLogList.push({
        type: 'add',
        dataType: 'obj',
        database: 'SysUser',
        checkOnlyKeyList: ['userName'],
        data: result,
        whereObj: null,
        remark: `SysUser表新增数据`,
      });

      const insertRoles = roles.map((e) => {
        return {
          roleId: e,
          userId: result.id,
        };
      });
      // 分配角色
      const list = await this.userRoleRepository.save(insertRoles);

      databaseOperationLogList.push({
        type: 'add',
        dataType: 'array',
        database: 'SysUserRole',
        checkOnlyKeyList: [],
        data: list,
        whereObj: null,
        remark: `SysUserRole表新增用户与角色关联关系`,
      });
    });

    await this.operationLogService.add(
      {
        type: 'add',
        msg: `用户模块新增用户`,
        databaseOperationLogList,
        remark: `新增用户${userName}`,
        mac,
        ip,
        syncHostSqlite: isNaN(Number(syncHostSqlite)) ? 0 : Number(syncHostSqlite),
      },
      user
    );
  }

  /**
   * 更新用户信息
   */
  async update(data, user, mac, ip, syncHostSqlite) {
    const databaseOperationLogList = [];

    const { roles, nickName, email, phone, remark, status, id, userName } = data;

    await this.entityManager.transaction(async (manager) => {
      await manager.update(
        SysUser,
        { id },
        {
          nickName,
          email,
          phone,
          remark,
          status,
          updateName: user.nickName,
          updateUserName: user.userName,
        }
      );

      databaseOperationLogList.push({
        type: 'update',
        dataType: 'obj',
        database: 'SysUser',
        checkOnlyKeyList: [],
        data: {
          nickName,
          email,
          phone,
          remark,
          status,
          updateName: user.nickName,
          updateUserName: user.userName,
        },
        whereObj: { id },
        remark: `SysUser表更新用户信息`,
      });
      // 先删除原来的角色关系
      await manager.delete(SysUserRole, { userId: id });

      databaseOperationLogList.push({
        type: 'delete',
        dataType: 'typeormObj',
        database: 'SysUserRole',
        checkOnlyKeyList: [],
        data: null,
        whereObj: {
          userId: {
            data: id,
            typeorm: null,
          },
        },
        remark: `SysUserRole表删除用户与角色关联关系`,
      });

      const insertRoles = roles.map((e) => {
        return {
          roleId: e,
          userId: id,
        };
      });
      // 重新分配角色
      const list2 = await manager.save(SysUserRole, insertRoles);

      databaseOperationLogList.push({
        type: 'add',
        dataType: 'array',
        database: 'SysUserRole',
        checkOnlyKeyList: [],
        data: list2,
        whereObj: null,
        remark: `SysUserRole表新增用户与角色关联关系`,
      });
    });

    await this.operationLogService.add(
      {
        type: 'update',
        msg: `用户模块更新用户信息`,
        databaseOperationLogList,
        remark: `更新用户${userName}`,
        mac,
        ip,
        syncHostSqlite: isNaN(Number(syncHostSqlite)) ? 0 : Number(syncHostSqlite),
      },
      user
    );
  }

  /**
   * 根据ID列表删除用户
   */
  async delete(userIdList, user, mac, ip, syncHostSqlite) {
    const rootUserId = await this.findRootUserId();

    const databaseOperationLogList = [];

    if (userIdList.includes(rootUserId)) {
      // throw new Error('can not delete root user!');
      throw new Error('can not delete root user!');
    }

    await this.userRoleRepository.delete({ userId: In(userIdList) });

    databaseOperationLogList.push({
      type: 'delete',
      dataType: 'typeormObj',
      database: 'SysUserRole',
      checkOnlyKeyList: [],
      data: null,
      whereObj: {
        userId: {
          data: userIdList,
          typeorm: 'In',
        },
      },
      remark: `SysUserRole表删除用户与角色关联关系`,
    });

    await this.userRepository.delete(userIdList);

    databaseOperationLogList.push({
      type: 'delete',
      dataType: 'array',
      database: 'SysUserRole',
      checkOnlyKeyList: [],
      data: null,
      whereObj: userIdList,
      remark: `SysUser表删除用户`,
    });

    await this.operationLogService.add(
      {
        type: 'delete',
        msg: `用户模块删除用户`,
        databaseOperationLogList,
        remark: `删除用户`,
        mac,
        ip,
        syncHostSqlite: isNaN(Number(syncHostSqlite)) ? 0 : Number(syncHostSqlite),
      },
      user
    );
  }

  /**
   * 查找超管的用户ID
   */
  async findRootUserId(): Promise<string> {
    const result = await this.userRoleRepository.findOne({
      where: { id: this.rootRoleId.toString() },
    });

    return result.userId;
  }

  /**
   * 直接更改管理员密码
   */
  async forceUpdatePassword(data, mac, ip, syncHostSqlite) {
    let { id, password } = data;

    const databaseOperationLogList = [];

    if (!password) {
      // 查找配置的初始密码
      const initPassword = await this.paramConfigService.findValueByCode(
        this.configService.get('initUserPassword')
      );

      password = initPassword ?? '123456';
    }

    const user = await this.userRepository.findOne({ where: { id } });
    if (isEmpty(user)) throw new ApiException(10017);

    const newPassword = this.util.md5(`${password}${user.psalt}`);
    await this.userRepository.update({ id }, { password: newPassword });

    databaseOperationLogList.push({
      type: 'update',
      dataType: 'obj',
      database: 'SysUser',
      checkOnlyKeyList: ['userName'],
      data: { password: newPassword },
      whereObj: { id },
      remark: `SysUser表更新用户密码`,
    });

    await this.operationLogService.add(
      {
        type: 'update',
        msg: `用户模块更新用户密码`,
        databaseOperationLogList,
        remark: `更新用户密码${data.userName}`,
        mac,
        ip,
        syncHostSqlite: isNaN(Number(syncHostSqlite)) ? 0 : Number(syncHostSqlite),
      },
      user
    );
  }

  /**
   * 查找用户信息
   * @param { id } 用户id
   */
  async info({ id }) {
    const user: any = await this.userRepository.findOne({ where: { id } });

    if (isEmpty(user)) throw new ApiException(10017);

    const roleRows = await this.userRoleRepository.find({
      where: { userId: user.id },
    });
    const roles = roleRows.map((e) => {
      return e.roleId;
    });
    delete user.password;
    delete user.psalt;

    return { ...user, roles };
  }

  async getAccountInfo(uid: string, ip?: string) {
    const user: SysUser = await this.userRepository.findOne({
      where: { id: uid },
    });
    if (isEmpty(user)) throw new ApiException(10017);

    const roleRows = await this.userRoleRepository.find({
      where: { userId: user.id },
    });
    const roleIds = roleRows.map((e) => {
      return {
        id: e.roleId,
      };
    });
    const roleList = await this.roleRepository.find({
      where: roleIds,
    });
    return {
      userName: user.userName,
      nickName: user.nickName,
      email: user.email,
      phone: user.phone,
      remark: user.remark,
      headImg: user.headImg,
      role: roleList,
      ip,
    };
  }

  /**
   * 更新个人信息
   */
  async updatePersonInfo(info, user): Promise<void> {
    await this.userRepository.save({
      id: user.uid,
      ...user,
      ...info,
    });
  }

  /**
   * 更改管理员密码
   */
  async updatePassword(dto, user): Promise<void> {
    const obj = await this.userRepository.findOne({ where: { id: user.uid } });
    if (isEmpty(obj)) throw new ApiException(10017);

    const comparePassword = this.util.md5(`${dto.oldPassword}${obj.psalt}`);
    // 原密码不一致，不允许更改
    if (obj.password !== comparePassword) throw new ApiException(10011);

    const password = this.util.md5(`${dto.newPassword}${obj.psalt}`);
    await this.userRepository.update({ id: user.uid }, { password });
    // await this.upgradePasswordV(obj.id);
  }

  /**
   * 升级用户版本密码
   */
  async upgradePasswordV(id: string): Promise<void> {
    // admin:passwordVersion:${param.id}
    const v = await this.redisService.getRedis().get(`admin:passwordVersion:${id}`);
    if (!isEmpty(v)) {
      await this.redisService.getRedis().set(`admin:passwordVersion:${id}`, Number.parseInt(v) + 1);
    }
  }

  /**
   * 禁用用户
   */
  async forbidden(uid: number): Promise<void> {
    await this.redisService.getRedis().del(`admin:passwordVersion:${uid}`);
    await this.redisService.getRedis().del(`admin:token:${uid}`);
    await this.redisService.getRedis().del(`admin:perms:${uid}`);
  }
}
